#!/bin/bash

# blog-admin 前端项目 Docker 部署脚本
# 用于 Jenkins 构建后自动部署

# 设置变量
# 使用 Jenkins 的 WORKSPACE 环境变量，如果没有则使用默认路径
WORKSPACE_DIR="${WORKSPACE:-/var/jenkins_home/workspace/blog-admin}"
# 项目目录：工作空间下的 blog-admin 子目录
PROJECT_DIR="${WORKSPACE_DIR}/blog-admin"

# 如果 blog-admin 子目录不存在，尝试使用工作空间根目录
if [ ! -d "$PROJECT_DIR" ]; then
    PROJECT_DIR="$WORKSPACE_DIR"
    echo "警告: 未找到 blog-admin 子目录，使用工作空间根目录: $PROJECT_DIR"
fi

CONTAINER_NAME="blog-admin"
BUILD_DIR="${PROJECT_DIR}/dist"
PORT=3000
LOG_DIR="/var/jenkins_home/logs"
LOG_FILE="${LOG_DIR}/blog-admin-deploy-$(date +%Y%m%d-%H%M%S).log"

# 创建日志目录
mkdir -p "$LOG_DIR"

# 日志函数：同时输出到控制台和日志文件
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

log_error() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $1" | tee -a "$LOG_FILE" >&2
}

log_success() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [SUCCESS] $1" | tee -a "$LOG_FILE"
}

log_info() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $1" | tee -a "$LOG_FILE"
}

# 检测 Docker 命令是否需要 sudo
DOCKER_CMD="docker"
if ! docker ps >/dev/null 2>&1; then
    if sudo docker ps >/dev/null 2>&1; then
        DOCKER_CMD="sudo docker"
        log_info "检测到需要使用 sudo 执行 Docker 命令"
    else
        log_error "无法访问 Docker，请检查权限配置"
        log_error "提示: 需要将 Jenkins 用户添加到 docker 组，或配置 sudo 无密码执行"
        exit 1
    fi
fi

log_info "========================================"
log_info "开始部署 blog-admin 前端服务..."
log_info "日志文件: $LOG_FILE"
log_info "========================================"

# 检查项目目录
if [ ! -d "$PROJECT_DIR" ]; then
    log_error "项目目录不存在: $PROJECT_DIR"
    exit 1
fi

# 进入项目目录
cd "$PROJECT_DIR" || {
    log_error "无法进入项目目录: $PROJECT_DIR"
    exit 1
}

# 检查是否需要构建（通过环境变量 FORCE_REBUILD 控制，默认为 true）
FORCE_REBUILD="${FORCE_REBUILD:-true}"

# 如果需要强制重新构建，或者构建目录不存在/为空，则执行构建
if [ "$FORCE_REBUILD" = "true" ] || [ ! -d "$BUILD_DIR" ] || [ -z "$(ls -A $BUILD_DIR 2>/dev/null)" ]; then
    if [ "$FORCE_REBUILD" = "true" ] && [ -d "$BUILD_DIR" ]; then
        log_info "检测到需要强制重新构建，删除旧的构建目录: $BUILD_DIR"
        rm -rf "$BUILD_DIR"
    fi
    
    log_info "开始构建项目..."
    
    # 检查并拉取 Node.js 镜像
    log_info "检查 Node.js 镜像..."
    if ! $DOCKER_CMD images --format "{{.Repository}}:{{.Tag}}" | grep -q "node:18-alpine"; then
        log_info "镜像不存在，开始拉取 node:18-alpine..."
        $DOCKER_CMD pull node:18-alpine >> "$LOG_FILE" 2>&1
        if [ $? -ne 0 ]; then
            log_error "镜像拉取失败"
            exit 1
        fi
    fi
    
    # 步骤1: 安装依赖（限制资源）
    log_info "步骤1: 安装依赖（限制内存 1.5GB，CPU 1.5核）..."
    $DOCKER_CMD run --rm \
        --memory="1.5g" \
        --memory-swap="1.5g" \
        --cpus="1.5" \
        -v "$PROJECT_DIR:/app" \
        -w /app \
        node:18-alpine \
        sh -c "npm config set registry https://registry.npmmirror.com && npm install --prefer-offline --no-audit --no-fund" >> "$LOG_FILE" 2>&1
    
    if [ $? -ne 0 ]; then
        log_error "依赖安装失败"
        exit 1
    fi
    log_success "依赖安装完成"
    
    # 步骤2: 构建项目（限制资源）
    log_info "步骤2: 构建项目（限制内存 1.5GB，CPU 1.5核）..."
    $DOCKER_CMD run --rm \
        --memory="1.5g" \
        --memory-swap="1.5g" \
        --cpus="1.5" \
        -v "$PROJECT_DIR:/app" \
        -w /app \
        -e NODE_OPTIONS="--max-old-space-size=1024" \
        node:18-alpine \
        npm run build >> "$LOG_FILE" 2>&1
    
    if [ $? -ne 0 ]; then
        log_error "构建失败"
        exit 1
    fi
    
    # 再次检查构建结果
    if [ ! -d "$BUILD_DIR" ] || [ -z "$(ls -A $BUILD_DIR)" ]; then
        log_error "构建失败：构建目录不存在或为空"
        exit 1
    fi
    
    log_success "项目构建完成: $BUILD_DIR"
else
    log_info "构建目录已存在且 FORCE_REBUILD=false，跳过构建步骤: $BUILD_DIR"
    log_info "提示: 如需强制重新构建，请设置环境变量 FORCE_REBUILD=true"
fi

# 检查端口是否被占用
check_port() {
    local port=$1
    log_info "检查端口 $port 是否被 Docker 容器占用..."
    
    # 使用 docker ps 检查所有运行中的容器端口映射
    local conflicting_containers=$($DOCKER_CMD ps --format "{{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}" | grep -E ":$port->|0\.0\.0\.0:$port|::$port" || true)
    
    if [ ! -z "$conflicting_containers" ]; then
        # 过滤掉同名的容器（允许自己的容器占用）
        local other_containers=$(echo "$conflicting_containers" | grep -v "^${CONTAINER_NAME}\t" || true)
        
        if [ ! -z "$other_containers" ]; then
            log_error "端口 $port 已被其他 Docker 容器占用:"
            echo -e "容器名称\t镜像\t状态\t端口映射" | tee -a "$LOG_FILE"
            echo -e "$other_containers" | tee -a "$LOG_FILE"
            return 1
        else
            log_info "端口 $port 仅被当前容器 $CONTAINER_NAME 使用（将重新创建）"
        fi
    fi
    
    log_success "端口 $port 未被其他容器占用，可用"
    return 0
}

# 检查容器是否存在
check_container() {
    local container_name=$1
    log_info "检查容器 $container_name 是否存在..."
    
    # 检查容器是否存在（包括停止的）
    if $DOCKER_CMD ps -a --format "{{.Names}}" | grep -q "^${container_name}$"; then
        local container_status=$($DOCKER_CMD ps -a --format "{{.Status}}" --filter "name=^${container_name}$")
        log_info "容器 $container_name 已存在，状态: $container_status"
        
        # 如果容器正在运行，先停止
        if $DOCKER_CMD ps --format "{{.Names}}" | grep -q "^${container_name}$"; then
            log_info "正在停止运行中的容器 $container_name..."
            $DOCKER_CMD stop $container_name >> "$LOG_FILE" 2>&1
            if [ $? -eq 0 ]; then
                log_success "容器 $container_name 已停止"
            else
                log_error "停止容器 $container_name 失败"
                return 1
            fi
        fi
        
        # 删除容器
        log_info "正在删除容器 $container_name..."
        $DOCKER_CMD rm $container_name >> "$LOG_FILE" 2>&1
        if [ $? -eq 0 ]; then
            log_success "容器 $container_name 已删除"
        else
            log_error "删除容器 $container_name 失败"
            return 1
        fi
    else
        log_info "容器 $container_name 不存在，无需清理"
    fi
    return 0
}

# 先检查并清理旧容器（这样会释放端口）
if ! check_container $CONTAINER_NAME; then
    log_error "容器清理失败，部署终止"
    exit 1
fi

# 等待端口释放
sleep 2

# 检查端口冲突（清理容器后再次检查）
if ! check_port $PORT; then
    log_error "端口冲突检测失败，部署终止"
    exit 1
fi

# 检查 Nginx 配置文件
NGINX_CONF="${PROJECT_DIR}/nginx.conf"
if [ ! -f "$NGINX_CONF" ]; then
    log_error "Nginx 配置文件不存在: $NGINX_CONF"
    exit 1
fi
log_success "Nginx 配置文件检查通过: $NGINX_CONF"

# 检查并拉取 Nginx 镜像
log_info "检查 Nginx 镜像..."
if ! $DOCKER_CMD images --format "{{.Repository}}:{{.Tag}}" | grep -q "nginx:alpine"; then
    log_info "镜像不存在，开始拉取 nginx:alpine..."
    $DOCKER_CMD pull nginx:alpine >> "$LOG_FILE" 2>&1
    if [ $? -eq 0 ]; then
        log_success "镜像拉取成功"
    else
        log_error "镜像拉取失败"
        exit 1
    fi
else
    log_success "镜像已存在"
fi

# 启动新容器
log_info "启动新容器 $CONTAINER_NAME..."
log_info "容器配置:"
log_info "  - 容器名称: $CONTAINER_NAME"
log_info "  - 端口映射: $PORT:80"
log_info "  - 静态文件目录: $BUILD_DIR"
log_info "  - Nginx 配置: $NGINX_CONF"

$DOCKER_CMD run -d \
    --name $CONTAINER_NAME \
    --restart=unless-stopped \
    -p $PORT:80 \
    -v $BUILD_DIR:/usr/share/nginx/html:ro \
    -v $NGINX_CONF:/etc/nginx/conf.d/default.conf:ro \
    nginx:alpine >> "$LOG_FILE" 2>&1

if [ $? -eq 0 ]; then
    log_success "容器启动命令执行成功"
else
    log_error "容器启动命令执行失败"
    exit 1
fi

# 等待容器启动
log_info "等待容器启动（3秒）..."
sleep 3

# 检查容器状态
log_info "检查容器运行状态..."
if $DOCKER_CMD ps --format "{{.Names}}" | grep -q "^${CONTAINER_NAME}$"; then
    log_success "========================================"
    log_success "部署成功！"
    log_success "容器名称: $CONTAINER_NAME"
    log_success "服务端口: $PORT"
    log_success "访问地址: http://服务器IP:$PORT"
    log_success "日志文件: $LOG_FILE"
    log_success "========================================"
    
    # 显示容器信息
    log_info "容器详细信息:"
    $DOCKER_CMD ps --filter "name=^${CONTAINER_NAME}$" | tee -a "$LOG_FILE"
    
    # 测试访问
    log_info "测试容器访问..."
    sleep 2
    if curl -s -o /dev/null -w "%{http_code}" http://localhost:$PORT | grep -q "200\|301\|302"; then
        log_success "容器访问测试通过"
    else
        log_info "容器访问测试: 可能需要等待几秒钟，或检查 Nginx 配置"
    fi
else
    log_error "========================================"
    log_error "部署失败！容器未启动"
    log_error "========================================"
    log_error "容器日志:"
    $DOCKER_CMD logs $CONTAINER_NAME 2>&1 | tee -a "$LOG_FILE"
    
    # 检查容器退出状态
    if $DOCKER_CMD ps -a --format "{{.Names}}" | grep -q "^${CONTAINER_NAME}$"; then
        local exit_code=$($DOCKER_CMD inspect --format='{{.State.ExitCode}}' $CONTAINER_NAME)
        log_error "容器退出代码: $exit_code"
    fi
    
    exit 1
fi

log_info "部署脚本执行完成"

